﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Microsoft.Win32;
using System.Data.SqlClient;
using System.Xml;
using System.Xml.Schema;
using System.IO;
using System.Reflection;

namespace System.Data.DataSetGenerator
{
    internal class Util
    {

        private static SqlCommandBuilder CommandBuilder = new SqlCommandBuilder();

        internal static bool StringToBool(String value)
        {
            bool result = false;
            bool.TryParse(value, out result);
            return result;
        }

        public static void InitializeObject(Object component)
        {
            foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(component))
            {
                pd.ResetValue(component);
            }
        }

        internal static void Validate(Stream xmlDocument, Stream xsdSchema)
        {
            StringBuilder b = new StringBuilder();
            bool error = false;
            ValidationEventHandler handler = (s, e) =>
            {
                if (e.Severity == XmlSeverityType.Error)
                {
                    error = true;
                    b.AppendFormat("Line {1}: {0}" + Environment.NewLine, e.Message, e.Exception.LineNumber);
                }
            };

            XmlTextReader textRdr = new XmlTextReader(xmlDocument);
            XmlValidatingReader rdr = new XmlValidatingReader(textRdr);
            rdr.ValidationEventHandler += handler;
            
            rdr.ValidationType = ValidationType.Schema;
            rdr.Schemas.Add(XmlSchema.Read(xsdSchema, handler));

            while (rdr.Read()) { }

            if (error)
                throw new ApplicationException(b.ToString());
        }

        public static void CloneProperties(Object from, Object to)
        {
            PropertyDescriptorCollection fromProperties = TypeDescriptor.GetProperties(from);
            PropertyDescriptorCollection toProperties = TypeDescriptor.GetProperties(to);

            foreach (PropertyDescriptor fromProperyDescriptor in fromProperties)
            {
                PropertyDescriptor toPropertyDescriptor = toProperties[fromProperyDescriptor.Name];
                if (toPropertyDescriptor != null)
                {
                    toPropertyDescriptor.SetValue(to, fromProperyDescriptor.GetValue(from));
                }
            }
        }

        internal static String GetPathToXSD_Exe()
        {
            RegistryKey r = Registry.LocalMachine;
            RegistryKey sdkKey = r.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0A", false);
            if (sdkKey == null)
                return "";
            return Convert.ToString(sdkKey.GetValue("CurrentInstallFolder")) + "bin\\xsd.exe";
        }

        internal static XmlDocument GetConfigXsdSchema(int majorVersion, int minorVersion)
        {
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("System.Data.DataSetGenerator.Config_" + majorVersion + "_" + minorVersion + ".xsd"))
            {
                if (stream == null)
                {
                    String errorMessage = String.Format("Cannot find XSD Schema for version {0}.{1}", majorVersion, minorVersion);
                    throw new ArgumentException(errorMessage, "version");
                }
                XmlDocument result = new XmlDocument();
                result.Load(stream);
                return result;
            }
        }

        internal static DataTable GetDataTableWithSchema(SqlConnection connection, String tableName)
        {
            using (SqlCommand cmd = connection.CreateCommand())
            {
                cmd.CommandText = String.Format("select * from {0} where 0 = 1", CommandBuilder.QuoteIdentifier(tableName));
                using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
                {
                    adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
                    DataTable result = new DataTable(tableName);
                    adapter.Fill(result);
                    return result;
                }
            }
        }

        internal static SqlCommand CreateDummySelectCommand(SqlConnection connection, String tableName)
        {
            SqlCommand result = connection.CreateCommand();
            result.CommandText = String.Format("select * from {0} where 0 = 1", CommandBuilder.QuoteIdentifier(tableName));
            return result;
        }

        internal static IEnumerable<String> GetTableNames(SqlConnection connection)
        {
            using (SqlCommand cmdAllTables = connection.CreateCommand())
            {
                cmdAllTables.CommandText = @"select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_TYPE = 'BASE TABLE' ORDER BY 1";
                using (SqlDataReader rdr = cmdAllTables.ExecuteReader())
                {
                    while (rdr.Read())
                    {
                        yield return rdr["TABLE_NAME"].ToString();
                    }
                }
            }
        }

        internal static IEnumerable<String> GetTableColumnNames(SqlConnection connection, String tableName, ColumnFilter filter)
        {
            using (SqlCommand cmdAllTables = connection.CreateCommand())
            {
                String sql = "select name as COLUMN_NAME from sys.columns where object_id = OBJECT_ID(@TABLE_NAME)";
                cmdAllTables.CommandText = sql;
                cmdAllTables.Parameters.AddWithValue("TABLE_NAME", tableName);

                if (filter != null)
                {
                    if (filter.IgnoreComputedColumns)
                    {
                        cmdAllTables.CommandText += " and is_computed <> 1 ";
                    }
                }

                cmdAllTables.CommandText += " order by column_id asc ";

                using (SqlDataReader rdr = cmdAllTables.ExecuteReader())
                {
                    while (rdr.Read())
                    {
                        yield return rdr["COLUMN_NAME"].ToString();
                    }
                }
            }
        }

        internal static SqlDataAdapter CreateDataAdapter(SqlConnection connection, String tableName, ConflictOption conflictOption, ColumnFilter columnFilter)
        {
            SqlCommand selectCommand = connection.CreateCommand();

            // Build SQL command for select clause, without any filter options (include all columns)
            var allColumnNames = GetTableColumnNames(connection, tableName, null).Select(name => CommandBuilder.QuoteIdentifier(name)).ToArray();
            String joinedColumnNames = String.Join(", ", allColumnNames);
            String selectCommandSql = String.Format("select " + joinedColumnNames + " from {0} where 0 = 1 ", CommandBuilder.QuoteIdentifier(tableName));

            // Build SQL command for insert, update and delete clause
            allColumnNames = GetTableColumnNames(connection, tableName, columnFilter).Select(name => CommandBuilder.QuoteIdentifier(name)).ToArray();
            joinedColumnNames = String.Join(", ", allColumnNames);
            String selectCommandForUpdateInsertDelete = String.Format("select " + joinedColumnNames + " from {0} where 0 = 1 ", CommandBuilder.QuoteIdentifier(tableName));
            selectCommand.CommandText = selectCommandForUpdateInsertDelete;

            // Generate commands
            SqlDataAdapter adapter = new SqlDataAdapter(selectCommand);
            SqlCommandBuilder commandBuilder = new SqlCommandBuilder(adapter);
            commandBuilder.ConflictOption = conflictOption;
            adapter.InsertCommand = commandBuilder.GetInsertCommand(true);
            adapter.UpdateCommand = commandBuilder.GetUpdateCommand(true);
            adapter.DeleteCommand = commandBuilder.GetDeleteCommand(true);

            adapter.SelectCommand.CommandText = selectCommandSql;

            return adapter;
        }
    }
}
